/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <PolicyService.h>
#include <Registrant.h>
#include <AsyncResult.h>
#include <expr.h>
#include <RegistrantList.h>
#include <OSMemory.h>
#include <OSLog.h>


#define EVENT_LOAD_EVENT            1
#define EVENT_LOAD_POLICY           2
#define EVENT_STATE_CHANGED         3

#define PARAMTER_CHECK_RETURN_VOID(x) \
    do{ \
        if (!x) { \
            return; \
        } \
    } while(0)

static boolean registerEvent(ArrayList* eventList, void* itemData, void* userData) {
    Event* event = (Event*)itemData;
    PolicyService* service = (PolicyService*)userData;
    if (event && service && service->mHandler) {
        event->registerStateChanged(service->mHandler, EVENT_STATE_CHANGED, (void*)service);
    }
    return TRUE;
}

static boolean unloadEvent(ArrayList* eventList, void* itemData, void* userData) {
    Event* event = (Event*)itemData;
    PolicyService* service = (PolicyService*)userData;
    if (event && service && service->mHandler) {
        event->unregisterStateChanged(service->mHandler);
        deleteEvent(event);
    }
    return TRUE;
}

static boolean registerVar(ArrayList* eventList, void* itemData, void* userData) {
    Event* event = (Event*)itemData;
    PolicyService* service = (PolicyService*)userData;
    if (event && service && service->mHandler) {
        char* name = event->getName();
        struct expr_var * var = expr_var((struct expr_var_list*)&service->mExprVarList, (const char*)event->getName(), strlen(name));
        if (var) {
            var->value = event->getState();
        }
    }
    return TRUE;
}

static void reloadEventValue(PolicyService* service, const char* name, boolean value) {
    if (service && service->mHandler) {
        struct expr_var * var = expr_var((struct expr_var_list*)&service->mExprVarList, name, strlen(name));
        if (var) {
            var->value = value;
        }
    }
}

static boolean dumpEvent(ArrayList* eventList, void* itemData, void* userData) {
    Event* event = (Event*)itemData;
    PolicyService* service = (PolicyService*)userData;
    if (event && service && service->mHandler) {
        char* name = event->getName();
        struct expr_var * var = expr_var((struct expr_var_list*)&service->mExprVarList, (const char*)name, strlen(name));
        if (var) {
            OS_LOG("dumpEvent name %s, value %f\n", name, var->value);
        }
    }
    return TRUE;
}

static boolean autoResetEventCb(ArrayList* eventList, void* itemData, void* userData) {
    Event* event = (Event*)itemData;
    if (event) {
        event->autoResetState();
    }
    return TRUE;
}

static void dumpEvents(PolicyService* service) {
    if (service == NULL || service->mEventList == NULL) {
        return;
    }
    arrayListEnum(service->mEventList, dumpEvent, (void*)service);
}

static void registerEvents(ArrayList* eventList, PolicyService* service) {
    arrayListEnum(eventList, registerEvent, (void*)service);
}

static void unloadEvents(ArrayList* eventList, PolicyService* service) {
    arrayListEnum(eventList, unloadEvent, (void*)service);
}


static void registerVars(ArrayList* eventList, PolicyService* service) {
    arrayListEnum(eventList, registerVar, (void*)service);
}

static void createExpression(PolicyService* service) {
    OS_LOG("createExpression %s\n", service->mPolicy);
    struct expr *e = expr_create(service->mPolicy, strlen(service->mPolicy), (struct expr_var_list *)&service->mExprVarList, NULL);
    service->mExpression = (void*)e;
}

static void handlerLoadEvent(Message* msg) {
    PARAMTER_CHECK_RETURN_VOID(msg && msg->userObj);
    PolicyService* service = (PolicyService*) msg->userObj;
    if (service->mEventCb) {
        service->mEventCb(service->mEventList);
    }
    registerEvents(service->mEventList, service);
    registerVars(service->mEventList, service);

    Message* nextMsg = messageObtain(service->mHandler, EVENT_LOAD_POLICY, NULL);
    nextMsg->userObj = service;
    messageSendToTarget(nextMsg);

}

static void handlerLoadPolicy(Message* msg) {
    PARAMTER_CHECK_RETURN_VOID(msg && msg->userObj);
    PolicyService* service = (PolicyService*) msg->userObj;
    if (service->mPolicyCb) {
        service->mPolicyCb(service->mPolicy, strlen(service->mPolicy) -1);
    }
    createExpression(service);
}

void setAction(PolicyService* service, Action* action) {
    if (service == NULL) {
        return;
    }
    service->mAction = action;
}

Event* getPolicyEvent(PolicyService* service, const char* name) {
    if (!service || !name) {
		return NULL;
	}
	int size = arrayListSize(service->mEventList);
	for(int i=0; i<size; i++) {
		Event* event = (Event*)arrayListGet(service->mEventList, i);
		if (strcmp(name, event->getName()) == 0) {
			return event;
		}
	}

	return NULL;
}

static void autoResetEvent(PolicyService* service) {
    if (service == NULL || service->mEventList == NULL) {
        return;
    }
    arrayListEnum(service->mEventList, autoResetEventCb, (void*)service);
}

static void onExcute(PolicyService* service) {
    if (service == NULL) {
        return;
    }
    dumpEvents(service);
    if (service->mExpression == NULL) {
        return;
    }
    boolean result = (boolean)expr_eval((struct expr *)service->mExpression);
    OS_LOG("onExcute result %d\n", result);
    if (result) {
        if (service->mAction != NULL) {
            onExcuteAction(service->mAction);
        }
        autoResetEvent(service);
    }
}


static void handlerStateChanged(Message* msg) {
    PARAMTER_CHECK_RETURN_VOID(msg && msg->obj);
    AsyncResult* ar = (AsyncResult*)msg->obj;
    PARAMTER_CHECK_RETURN_VOID(ar->result);
    PolicyService* service = (PolicyService*) ar->userObj;
	EventData* eventData = (EventData*)ar->result;
    OS_LOG("handlerStateChanged event %s state %d\n", eventData->mNickName, eventData->mState);
	reloadEventValue(service, eventData->mNickName, eventData->mState);
    onExcute(service);
    deleteAsyncResultMessage(msg);
}

static void policyServiceHandlerMessage(Message* msg) {
    OS_LOG("policyServiceHandlerMessage msg->what %d\n", msg->what);
    switch (msg->what) {
        case EVENT_LOAD_EVENT:
            handlerLoadEvent(msg);
            break;
        case EVENT_LOAD_POLICY:
            handlerLoadPolicy(msg);
            break;
        case EVENT_STATE_CHANGED:
            handlerStateChanged(msg);
            break;
        default:
            break;
    }
}

PolicyService* newPolicyService(onLoadEvent loadEvent, onLoadPolicy loadPolicy) {
    boolean ret = FALSE;
    PolicyService* service = (PolicyService*)OS_MALLOC(sizeof(PolicyService));
    if (!service) {
        goto exit;
    }
    memset(service, 0, sizeof(PolicyService));

    service->mEventList = newArrayList();
    if (!service->mEventList) {
        goto exit;
    }

    service->mHandler = newHandler(policyServiceHandlerMessage);
    if (!service->mHandler) {
        goto exit;
    }

    service->mEventCb = loadEvent;
    service->mPolicyCb = loadPolicy;

    Message* msg = messageObtain(service->mHandler, EVENT_LOAD_EVENT, NULL);
    msg->userObj = service;
    messageSendToTarget(msg);

    ret = TRUE;
exit:
    if (!ret) {
        if (service) {
            if (service->mHandler) {
                deleteHandler(service->mHandler);
            }
            if (service->mEventList) {
                deleteArrayList(service->mEventList);
            }

            OS_FREE(service);
            service = NULL;
        }

    }

    return service;
}

void deletePolicyService(PolicyService* service) {
    if (service) {
        if (service->mHandler) {
            deleteHandler(service->mHandler);
        }
        if (service->mEventList) {
            unloadEvents(service->mEventList, service);
            deleteArrayList(service->mEventList);
        }

        if (service->mExpression) {
            expr_destroy((struct expr*)service->mExpression, (struct expr_var_list*)&service->mExprVarList);
        }

        if (service->mAction) {
            deleteAction(service->mAction);
        }

        OS_FREE(service);
        service = NULL;
    }

}

